/**
 * Decorator
 *
 * @file 装饰包装器（装饰器模式）
 * @todo 目前这个库还有错误，无法正常，需要修复！！！
 * */

/**
 * Decorator - 装饰包装器类
 *
 * @class
 * @classdesc 装饰包装器用于包装一个函数，使得执行这个被包装的函数之前或之后执行一系列指定的方法，使用范畴包括且不仅限于表单验证，方法守卫，方法校验等
 * @param { Function } wrappedFun - 被包装函数
 * @param { Object } thisDirection - 被包装执行方法内 this 指向对象
 * @extends Object
 * */
export const Decorator = function (wrappedFun, thisDirection) {
    // 前置执行方法记录栈
    const beforeWrapperFunStack = {};
    // 后置执行方法记录栈
    const afterWrapperFunStack = {};

    return {
        /**
         * before - 添加被包装函数前置执行方法包装记录
         *
         * @function
         * @param { Function } beforeFun - 前置执行方法
         * @param { Object } thisDirection - 前置执行方法内 this 指向对象
         * @param { any } args - 前置执行方法的参数
         * @memberOf Decorator
         * */
        before: function (beforeFun, thisDirection, ...args) {
            beforeWrapperFunStack[beforeFun.toString()] = {
                fun: beforeFun,
                args: args,
                thisDirection: thisDirection
            };
        },
        /**
         * after - 添加被包装函数后置执行方法包装记录
         *
         * @function
         * @param { Function } afterFun - 后置执行方法
         * @param { Object } thisDirection - 后置执行方法内 this 指向对象
         * @param { any } args - 后置执行方法的参数
         * @memberOf Decorator
         * */
        after: function (afterFun, thisDirection, ...args) {
            afterWrapperFunStack[afterFun.toString()] = {
                fun: afterFun,
                args: args,
                thisDirection: thisDirection
            };
        },
        /**
         * run - 运行包装函数
         *
         * @function
         * @param { any } args - 传入被包装函数执行参数
         * @return { Object } 返回包装函数的运行结果对象
         * @memberOf Decorator
         * */
        run: function (...args) {
            // 记录所有执行方法执行结果数组
            const allFunResult = [];

            // 执行前置执行方法
            const beforeWrapperFunResult = [];
            Object.keys(beforeWrapperFunStack).forEach(wrapperFunDataItem => {
                const result = wrapperFunDataItem.fun.apply(wrapperFunDataItem.thisDirection, ...wrapperFunDataItem.args);
                beforeWrapperFunResult.push(result);
            });
            allFunResult.push(...beforeWrapperFunResult);

            // 执行被包装函数
            const wrappedFunResult = wrappedFun.apply(thisDirection, args);
            allFunResult.push(wrappedFunResult);

            // 执行后置执行方法
            const afterWrapperFunResult = [];
            Object.keys(afterWrapperFunStack).forEach(wrapperFunDataItem => {
                const result = wrapperFunDataItem.fun.apply(wrapperFunDataItem.thisDirection, ...wrapperFunDataItem.args);
                afterWrapperFunResult.push(result);
            });
            allFunResult.push(...afterWrapperFunResult);

            return {
                beforeWrapperFunResult: beforeWrapperFunResult,
                wrappedFunResult: wrappedFunResult,
                afterWrapperFunResult: afterWrapperFunResult,
                allFunResult: allFunResult,
            }
        },
        /**
         * getWrapperFun - 获取包装函数
         *
         * @function
         * @param { any } args - 传入被包装函数执行参数
         * @return { Function } 包装函数
         * @memberOf Decorator
         * */
        getWrapperFun: function (...args) {
            return function () {
                try {
                    // 遍历执行前置执行方法
                    Object.keys(beforeWrapperFunStack).forEach(wrapperFunDataItem => {
                        wrapperFunDataItem.fun.apply(wrapperFunDataItem.thisDirection, ...wrapperFunDataItem.args);
                    });

                    // 执行被包装函数
                    wrappedFun.apply(thisDirection, args);

                    // 遍历执行后置执行方法
                    Object.keys(afterWrapperFunStack).forEach(wrapperFunDataItem => {
                        wrapperFunDataItem.fun.apply(wrapperFunDataItem.thisDirection, ...wrapperFunDataItem.args);
                    });

                    return true;
                } catch (err) {
                    console.log(err);
                    return false;
                }
            }
        }
    }
}

export default Decorator;
